﻿using Emgu.CV;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 仪表识别2
{
    public class Util
    {
        public static PointF[] PointToPointF(Point[] pf)
        {
            PointF[] aaa = new PointF[pf.Length];
            int num = 0;
            foreach (var point in pf)
            {
                aaa[num].X = (int)point.X;
                aaa[num++].Y = (int)point.Y;
            }
            return aaa;
        }
        public static Point PointFToPoint(PointF pf)
        {
            return new Point((int)Math.Round(pf.X),(int)Math.Round(pf.Y));
        }
        public static CircleF[] CircleFSort(CircleF[] circles)
        {
            if (circles == null)
            {
                return null;
            }
            bool isOK = false;
            do
            {
                isOK = true;
                for (int i = 0; i < circles.Length - 1; i++)
                {
                    if (circles[i].Radius < circles[i + 1].Radius)
                    {
                        CircleF temp = circles[i];
                        circles[i] = circles[i + 1];
                        circles[i + 1] = temp;
                        isOK = false;
                    }
                }
            }
            while (!isOK);
            return circles;
        }
        public static Point AvrPoint(Point[] point)
        {
            Point outpoint = new Point(0, 0);
            int p_x = 0;
            int p_y = 0;
            int n = 0;
            foreach (Point p in point)
            {
                if (p.X != 0 && p.Y != 0)
                {
                    p_x += p.X;
                    p_y += p.Y;
                    n++;
                }
            }
            if (n != 0)
            {
                p_x = p_x / n;
                p_y = p_y / n;
            }
            outpoint = new Point(p_x, p_y);
            return outpoint;
        }
        public static PointF AvrPoint(PointF[] point)
        {
            PointF outpoint = new Point(0, 0);
            float p_x = 0;
            float p_y = 0;
            int n = 0;
            foreach (PointF p in point)
            {
                if (p.X != 0 && p.Y != 0)
                {
                    p_x += p.X;
                    p_y += p.Y;
                    n++;
                }
            }
            if (n != 0)
            {
                p_x = p_x / n;
                p_y = p_y / n;
            }
            outpoint = new PointF(p_x, p_y);
            return outpoint;
        }
        public static double Distance(Point p1, Point p2)
        {
            double result = 0;
            result = Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
            return result;
        }
        public static double Distance(PointF p1, PointF p2)
        {
            double result = 0;
            result = Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
            return result;
        }
        public static LineSegment2D[] GetInCicrleLine(LineSegment2D[] lines,CircleF? circle)
        {
            LineSegment2D[] res = new LineSegment2D[lines.Length];
            if (circle != null)
            {
                float r = circle.Value.Radius;
                PointF center = circle.Value.Center;
                int n = 0;
                for (int i = 0; i < lines.Length; i++)
                {
                    LineSegment2D line = lines[i];
                    if (Distance(center, line.P1) < r && Distance(center, line.P2) < r )//在圆内
                    {
                        if(lines[i].Length < r)//长度小于半径
                        {
                            if (Distance(center, line.P1) < 30 || Distance(center, line.P2) < 30)//有一个点靠近圆心
                            {
                                res[n++] = line; 
                            }
                        }
                        
                    }
                }
            }
            return res;
        }
        public static LineSegment2D[] LineSort(LineSegment2D[] lines)
        {
            if (lines == null)
            {
                return null;
            }
            bool isOK = false;
            do
            {
                isOK = true;
                for (int i = 0; i < lines.Length - 1; i++)
                {
                    if (lines[i].Length < lines[i + 1].Length)
                    {
                        LineSegment2D temp = lines[i];
                        lines[i] = lines[i + 1];
                        lines[i + 1] = temp;
                        isOK = false;
                    }
                }
            }
            while (!isOK);
            return lines;
        }
        //public static Image<Ycc, byte> ToYcc(Image<Bgr, byte> pic)
        //{
        //    Image<Ycc, byte> ycc = pic.Convert<Ycc, byte>();
        //    for (int i = 0; i < pic.Height; i++)
        //        for (int j = 0; j < pic.Width; j++)
        //        {
        //            if (ycc[i, j].Cr > Param.Cr_Min &&
        //                ycc[i, j].Cr < Param.Cr_Max &&
        //                ycc[i, j].Cb > Param.Cb_Min &&
        //                ycc[i, j].Cb < Param.Cb_Max)
        //            {
        //                ycc[i, j] = RgbToYcc(new Rgb(Color.Black));// new Ycc(0, 123, 123);
        //            }
        //            else
        //            {
        //                //ycc[i, j] = RgbToYcc(new Rgb(Color.White));
        //            }
        //        }
        //    return ycc;
        //}
        public static Ycc RgbToYcc(Rgb rgb)
        {
            Ycc ycc = new Ycc();
            double y = 0.299 * rgb.Red + 0.587 * rgb.Green + 0.114 * rgb.Blue;
            double cb = -0.16874 * rgb.Red - 0.33126 * rgb.Green + 0.50000 * rgb.Blue + 128;
            double cr = 0.50000 * rgb.Red - 0.41869 * rgb.Green - 0.08131 * rgb.Blue + 128;
            ycc.Y = y;
            ycc.Cb = cb;
            ycc.Cr = cr;
            return ycc;
        }
        public static Rgb YccToRgb(Ycc ycc)
        {
            Rgb rgb = new Rgb();
            rgb.Red = 1.164 * (ycc.Y - 16) + 1.596 * (ycc.Cr - 128);
            rgb.Green = 1.164 * (ycc.Y - 16) - 0.392 * (ycc.Cb - 128) - 0.813 * (ycc.Cr - 128);
            rgb.Blue = 1.164 * (ycc.Y - 16) + 2.017 * (ycc.Cb - 128);
            return rgb;
        }
        public delegate int compar<T>(T obj1, T obj2);
        public static T getMax<T>(T[] obj, compar<T> com)
        {
            T em = obj[0];
            for (int i = 0; i < obj.Length; i++)
            {

                if (com(em, obj[i]) < 0)
                {
                    em = obj[i];
                }
            }
            return em;
        }
        public static T getMin<T>(T[] obj, compar<T> com)
        {
            T em = obj[0];
            for (int i = 0; i < obj.Length; i++)
            {

                if (com(em, obj[i]) > 0)
                {
                    em = obj[i];
                }
            }
            return em;
        }
        public static int getMinValueIndex<T>(T[] obj, compar<T> com)
        {
            T em = obj[0];
            int index = 0;
            for (int i = 0; i < obj.Length; i++)
            {

                if (com(em, obj[i]) > 0)
                {
                    em = obj[i];
                    index = i;
                }
            }
            return index;
        }
        public static bool isInTriangle(PointF A,PointF B,PointF C,PointF P)
        {
            Vector3 AB = new Vector3(B.X - A.X, B.Y - A.Y, 0);
            Vector3 BC = new Vector3(C.X - B.X, C.Y - B.Y, 0);
            Vector3 AC = new Vector3(C.X - A.X, C.Y - A.Y, 0);

            Vector3 AP = new Vector3(P.X - A.X, P.Y - A.Y, 0);
            Vector3 BP = new Vector3(P.X - B.X, P.Y - B.Y, 0);
            Vector3 CP = new Vector3(P.X - C.X, P.Y - C.Y, 0);

            float u = ((AB * AB) * (AP * AC) - ((AC * AB) * (AP * AB))) / ((AC * AC) * (AB * AB) - (AC * AB) * (AB * AC));
            float v = ((AC * AC) * (AP * AB) - ((AC * AB) * (AP * AC))) / ((AC * AC) * (AB * AB) - (AC * AB) * (AB * AC));

            //Log.info("u="+u);
            //Log.info("V="+v);

            return u >= 0 && u <= 1 && v >= 0 && u <= 1 && (u + v) <= 1;
        }

        public static PointF getIntersection(PointF lineFirstStar, PointF lineFirstEnd, PointF lineSecondStar, PointF lineSecondEnd)
        {
            /*
             * L1，L2都存在斜率的情况：
             * 直线方程L1: ( y - y1 ) / ( y2 - y1 ) = ( x - x1 ) / ( x2 - x1 ) 
             * => y = [ ( y2 - y1 ) / ( x2 - x1 ) ]( x - x1 ) + y1
             * 令 a = ( y2 - y1 ) / ( x2 - x1 )
             * 有 y = a * x - a * x1 + y1   .........1
             * 直线方程L2: ( y - y3 ) / ( y4 - y3 ) = ( x - x3 ) / ( x4 - x3 )
             * 令 b = ( y4 - y3 ) / ( x4 - x3 )
             * 有 y = b * x - b * x3 + y3 ..........2
             * 
             * 如果 a = b，则两直线平等，否则， 联解方程 1,2，得:
             * x = ( a * x1 - b * x3 - y1 + y3 ) / ( a - b )
             * y = a * x - a * x1 + y1
             * 
             * L1存在斜率, L2平行Y轴的情况：
             * x = x3
             * y = a * x3 - a * x1 + y1
             * 
             * L1 平行Y轴，L2存在斜率的情况：
             * x = x1
             * y = b * x - b * x3 + y3
             * 
             * L1与L2都平行Y轴的情况：
             * 如果 x1 = x3，那么L1与L2重合，否则平等
             * 
            */
            float a = 0, b = 0;
            int state = 0;
            if (lineFirstStar.X != lineFirstEnd.X)
            {
                a = (lineFirstEnd.Y - lineFirstStar.Y) / (lineFirstEnd.X - lineFirstStar.X);
                state |= 1;
            }
            if (lineSecondStar.X != lineSecondEnd.X)
            {
                b = (lineSecondEnd.Y - lineSecondStar.Y) / (lineSecondEnd.X - lineSecondStar.X);
                state |= 2;
            }
            switch (state)
            {
                case 0: //L1与L2都平行Y轴
                    {
                        if (lineFirstStar.X == lineSecondStar.X)
                        {
                            //throw new Exception("两条直线互相重合，且平行于Y轴，无法计算交点。");
                            return new PointF(0, 0);
                        }
                        else
                        {
                            //throw new Exception("两条直线互相平行，且平行于Y轴，无法计算交点。");
                            return new PointF(0, 0);
                        }
                    }
                case 1: //L1存在斜率, L2平行Y轴
                    {
                        float x = lineSecondStar.X;
                        float y = (lineFirstStar.X - x) * (-a) + lineFirstStar.Y;
                        return new PointF(x, y);
                    }
                case 2: //L1 平行Y轴，L2存在斜率
                    {
                        float x = lineFirstStar.X;
                        float y = (lineSecondStar.X - x) * (-b) + lineSecondStar.Y;
                        return new PointF(x, y);
                    }
                case 3: //L1，L2都存在斜率
                    {
                        if (a == b)
                        {
                            // throw new Exception("两条直线平行或重合，无法计算交点。");
                            return new PointF(0, 0);
                        }
                        float x = (a * lineFirstStar.X - b * lineSecondStar.X - lineFirstStar.Y + lineSecondStar.Y) / (a - b);
                        float y = a * x - a * lineFirstStar.X + lineFirstStar.Y;
                        return new PointF(x, y);
                    }
            }
            // throw new Exception("不可能发生的情况");
            return new PointF(0, 0);
        }
        /// <summary>
        /// 已知三点求夹角
        /// </summary>
        /// <param name="cen"></param>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        public static double AngleOfPoint(PointF cen, PointF first, PointF second)
        {
            const double M_PI = Math.PI;

            double ma_x = first.X - cen.X;
            double ma_y = first.Y - cen.Y;
            double mb_x = second.X - cen.X;
            double mb_y = second.Y - cen.Y;
            double v1 = (ma_x * mb_x) + (ma_y * mb_y);
            double ma_val = Math.Sqrt(ma_x * ma_x + ma_y * ma_y);
            double mb_val = Math.Sqrt(mb_x * mb_x + mb_y * mb_y);
            double cosM = v1 / (ma_val * mb_val);
            //double sinM = Math.Sqrt(1-cosM*cosM);
            double angleAMB = Math.Acos(cosM) * 180 / M_PI;

            return angleAMB;
        }

        public static void delay(int mm)
        {
            DateTime current = DateTime.Now;
            while (current.AddMilliseconds(mm) > DateTime.Now)
            {
                Application.DoEvents();
            }
            return;
        }
    }
}
